package mysgoog.brown;

import java.io.*;
import java.util.*;

public class Gtp
{
    /** Maximum allowed line length in GTP. */
    public static final int GTP_BUFSIZE = 1000;
    /** Status returned from callback functions. */
    public static final int GTP_QUIT = -1;
    public static final int GTP_OK = 0;
    public static final int GTP_FATAL = 1;
    /** Whether the GTP command was successful. */
    public static final int GTP_SUCCESS = 0;
    public static final int GTP_FAILURE = 1;
    /** Function pointer for callback functions. */
    // TODO
    // typedef int (*gtp_fn_ptr)(char *s);
    /** Function pointer for vertex transform functions. */
    // typedef void (*gtp_transform_ptr)(int ai, int aj, int *bi, int *bj);
    /*
     * These are copied from gnugo.h. We don't include this file in order to
     * remain as independent as possible of GNU Go internals.
     */
    public static final int EMPTY = 0;
    public static final int WHITE = 1;
    public static final int BLACK = 2;
    /*
     * We need to keep track of the board size in order to be able to convert
     * between coordinate descriptions. We could also have passed the board size
     * in all calls needing it, but that would be unnecessarily inconvenient.
     */
    private static int gtp_boardsize = -1;
    /* Vertex transformation hooks. */
    // TODO static gtp_transform_ptr vertex_transform_input_hook = NULL;
    // TODO static gtp_transform_ptr vertex_transform_output_hook = NULL;
    /*
     * Current id number. We keep track of this internally rather than pass it
     * to the functions processing the commands, since those can't do anything
     * useful with it anyway.
     */
    static int current_id;

    // static String commandString = "([\\d* ])(.*)([ .*]+)";
    // static Pattern commandPattern = Pattern.compile(commandString);
    static void gtp_main_loop(GtpCommand commands[], InputStream gtp_input,
            OutputStream gtp_dump_commands) throws IOException
    {
        String line;
        String command;
        String arg;
        char[] p = new char[GTP_BUFSIZE];
        int pindex = 0;
        int i;
        int status = GTP_OK;
        BufferedReader reader = new BufferedReader(new InputStreamReader(gtp_input));
        PrintWriter writer = null;
        if (gtp_dump_commands != null)
            writer = new PrintWriter(gtp_dump_commands);
        while (status == GTP_OK)
        {
            /* Read a line from gtp_input. */
            line = reader.readLine();
            // System.out.println(line);
            // XXX fixed by zdq, output here is dump not stdout
            Lib.log(line);

            if (line == null)
                break; /* EOF or some error */
            Lib.assertTrue(line.length() < GTP_BUFSIZE);
            if (writer != null)
                writer.println(line);
            /* Preprocess the line. */
            pindex = 0;
            for (i = 0; i < line.length(); i++)
            {
                char c = line.charAt(i);
                /* Convert HT (9) to SPACE (32). */
                if (c == 9)
                {
                    p[pindex] = 32;
                    pindex++;
                }
                /*
                 * Remove CR (13) and all other control characters except LF
                 * (10).
                 */
                else if ((c > 0 && c <= 9) || (c >= 11 && c <= 31) || c == 127)
                    continue;
                /* Remove comments. */
                else if (c == '#')
                    break;
                /* Keep ordinary text. */
                else
                {
                    p[pindex] = c;
                    pindex++;
                }
            }
            /* Terminate string. */
            p[pindex] = 0;
            String pp = String.copyValueOf(p, 0, pindex);
            // Matcher commandMatcher = commandPattern.matcher(pp);
            Scanner sc = new Scanner(pp);
            if (sc.hasNextInt())
                current_id = sc.nextInt();
            else
                current_id = -1;
            if (sc.hasNext())
                command = sc.next();
            else
                continue;
            if (sc.hasNextLine())
                arg = sc.nextLine();
            else
                arg = "";
            /*
             * Look for an identification number. if (sscanf(p, "%d%n",
             * &current_id, &n) == 1) p += n; else current_id = -1; / No
             * identification number.
             * 
             * / Look for command name. if (sscanf(p, " %s %n", command, &n) <
             * 1) continue; / Whitespace only on this line, ignore. p += n;
             * 
             * / Search the list of commands and call the corresponding function
             * if it's found.
             */
            for (i = 0; commands[i].name != null; i++)
            {
                // System.out.println(command);
                if (command.equals(commands[i].name))
                {
                    status = commands[i].execute(arg);
                    break;
                }
            }
            if (commands[i].name == null)
                gtp_failure("unknown command");
            if (status == GTP_FATAL)
                gtp_panic();
        }
    }

    /** Set the board size used in coordinate conversions. */
    static void gtp_internal_set_boardsize(int size)
    {
        gtp_boardsize = size;
    }

    /**
     * If you need to transform the coordinates on input or output, use these
     * functions to set hook functions which are called any time coordinates are
     * read or about to be written. In GNU Go this is used to simulate rotated
     * boards in regression tests.
     */
    // TODO void gtp_set_vertex_transform_hooks(gtp_transform_ptr in,
    // gtp_transform_ptr out)
    // {
    // vertex_transform_input_hook = in;
    // vertex_transform_output_hook = out;
    // }
    /** This currently works exactly like printf. */
    static void gtp_printf(String s)
    {
        // XXX fixed by zdq
        System.out.print(s);
    }

    /**
     * Write success or failure indication plus identity number if one was
     * given.
     */
    static void gtp_start_response(int status)
    {
        if (status == GTP_SUCCESS)
            gtp_printf("=");
        else
            gtp_printf("?");
        if (current_id < 0)
            gtp_printf(" ");
        else
            gtp_printf(current_id + " ");
    }

    /** Finish a GTP response by writing a double newline and returning GTP_OK. */
    static int gtp_finish_response()
    {
        gtp_printf("\n\n");
        return GTP_OK;
    }

    /**
     * Write a full success response. Except for the id number, the call is just
     * like one to printf.
     */
    static int gtp_success(String s)
    {
        // XXX fixed by zdq
        gtp_start_response(GTP_SUCCESS);
        System.out.print(s);
        return gtp_finish_response();
    }

    /** Write a full failure response. The call is identical to gtp_success. */
    static int gtp_failure(String s)
    {
        // XXX fixed by zdq
        gtp_start_response(GTP_FAILURE);
        System.out.print(s);
        return gtp_finish_response();
    }

    /** Write a panic message. */
    static void gtp_panic()
    {
        gtp_printf("! panic\n\n");
    }

    /**
     * Convert a string describing a color, "b", "black", "w", or "white", to
     * GNU Go's integer representation of colors. Return the number of
     * characters read from the string s.
     */
    static int gtp_decode_color(String s, int[] color)
    {
        String color_string = "";
        Lib.assertTrue(gtp_boardsize > 0);
        int n = 0;
        Scanner sc = new Scanner(s);
        if (!sc.hasNext())
            return 0;
        // TODO do not consider read 6 characters at most %6s
        color_string = sc.next();
        n = s.indexOf(color_string) + color_string.length();
        color_string = color_string.toLowerCase();
        if (color_string.equals("b") || color_string.equals("black"))
            color[0] = BLACK;
        else if (color_string.equals("w") || color_string.equals("white"))
            color[0] = WHITE;
        else
            return 0;
        return n;
    }

    /**
     * Convert an intersection given by a string to two coordinates according to
     * GNU Go's convention. Return the number of characters read from the string
     * s.
     */
    static int gtp_decode_coord(String s, int[] i, int[] j)
    {
        char column;
        int row = -1;
        int n = 0;
        Lib.assertTrue(gtp_boardsize > 0);
        if (s == null)
            return 0;
        if (s.trim().length() < 1)
            return 0;
        column = s.trim().charAt(0);
        /** zl add it himself */
        if (!((column >= 'a' && column <= 'z') || (column >= 'A' && column <= 'Z')))
            return 0;
        Scanner sc = new Scanner(s.substring(s.indexOf(column) + 1));
        if (sc.hasNextInt())
            row = sc.nextInt();
        else
            return 0;
        if (column == 'i' || column == 'I')
            return 0;
        j[0] = Character.toLowerCase(column) - 'a';
        if (Character.toLowerCase(column) > 'i')
            --j[0];
        i[0] = gtp_boardsize - row;
        if (i[0] < 0 || i[0] >= gtp_boardsize || j[0] < 0 || j[0] >= gtp_boardsize)
            return 0;
        // if (vertex_transform_input_hook != null)
        // (*vertex_transform_input_hook)(*i, *j, i, j);
        String srow = new Integer(row).toString();
        n = s.indexOf(srow) + srow.length();
        return n;
    }

    /**
     * Convert a move, i.e. "b" or "w" followed by a vertex to a color and
     * coordinates. Return the number of characters read from the string s. The
     * vertex may be "pass" and then the coordinates are set to (-1, -1).
     */
    static int gtp_decode_move(String s, int[] color, int[] i, int[] j)
    {
        int n1, n2;
        int k;
        Lib.assertTrue(gtp_boardsize > 0);
        s = s.trim();
        n1 = gtp_decode_color(s, color);
        if (n1 == 0)
            return 0;
        n2 = gtp_decode_coord(s.substring(n1), i, j);
        if (n2 == 0)
        {
            Scanner sc = new Scanner(s.substring(n1));
            // TODO do not consider read 5 characters at most %5s
            String buf = sc.next();
            buf = buf.toLowerCase();
            if (!buf.equals("pass"))
                return 0;
            i[0] = -1;
            j[0] = -1;
            n2 = s.substring(n1).indexOf(buf) + buf.length();
        }
        return n1 + n2;
    }

    /**
     * This a bubble sort. Given the expected size of the sets to sort, it's
     * probably not worth the overhead to set up a call to qsort.
     */
    static void sort_moves(int n, int movei[], int movej[])
    {
        int b, a;
        for (b = n - 1; b > 0; b--)
        {
            for (a = 0; a < b; a++)
            {
                if (movei[a] > movei[b] || (movei[a] == movei[b] && movej[a] > movej[b]))
                {
                    int tmp;
                    tmp = movei[b];
                    movei[b] = movei[a];
                    movei[a] = tmp;
                    tmp = movej[b];
                    movej[b] = movej[a];
                    movej[a] = tmp;
                }
            }
        }
    }

    /**
     * Write a number of space separated vertices. The moves are sorted before
     * being written.
     */
    static void gtp_print_vertices(int n, int movei[], int movej[])
    {
        int k;
        int ri, rj;
        Lib.assertTrue(gtp_boardsize > 0);
        sort_moves(n, movei, movej);
        for (k = 0; k < n; k++)
        {
            if (k > 0)
                gtp_printf(" ");
            if (movei[k] == -1 && movej[k] == -1)
                gtp_printf("PASS");
            else if (movei[k] < 0 || movei[k] >= gtp_boardsize || movej[k] < 0
                    || movej[k] >= gtp_boardsize)
                gtp_printf("??");
            else
            {
                // TODO if (vertex_transform_output_hook != NULL)
                // (*vertex_transform_output_hook)(movei[k], movej[k], &ri,
                // &rj);
                // else {
                ri = movei[k];
                rj = movej[k];
                // }
                System.out.printf("%c%d", 'A' + rj + ((rj >= 8) ? 1 : 0), (gtp_boardsize - ri));
            }
        }
    }

    /* Write a single move. */
    static void gtp_print_vertex(int i, int j)
    {
        int[] ari = new int[] { i };
        int[] arj = new int[] { j };
        gtp_print_vertices(1, ari, arj);
        i = ari[0];
        j = arj[0];
    }
}
